package com.sailing.xjpb.orm.repository;

import com.sailing.xjpb.common.ClassUtils;
import com.sailing.xjpb.common.DataConverter;
import com.sailing.xjpb.common.SailingAssert;
import com.sailing.xjpb.common.exception.BusinessException;
import com.sailing.xjpb.common.exception.ConvertException;
import com.sailing.xjpb.orm.query.QueryCondition;
import com.sailing.xjpb.orm.utils.AliasToBeanResultTransformer;
import com.sailing.xjpb.orm.utils.CamelCaseToMapResultTransformer;
import com.sailing.xjpb.orm.utils.SqlUtils;
import com.sailing.xjpb.orm.utils.XtPrimitiveWrapperResultTransformer;
import org.hibernate.transform.ResultTransformer;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.support.PageableExecutionUtils;

import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.Query;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Supplier;

/**
 * EntityManager完成对数据库的query和update-对jpa进行补充
 *
 * @author YaoWei
 */
@SuppressWarnings("unchecked")
public class SailingRepository {

    private final EntityManager entityManager;

    public SailingRepository(EntityManager entityManager) {
        this.entityManager = entityManager;
    }

    /**
     * 执行sql语句
     *
     * @param sql sql语句
     * @return 受影响行数
     */
    public int executeUpdate(String sql) {
        return this.executeUpdate(sql, new HashMap<>(2));
    }

    /**
     * 执行sql语句
     *
     * @param sql    sql语句
     * @param params 注入参数
     * @return 受影响行数
     */
    public int executeUpdate(String sql, Map<String, Object> params) {
        Query query = entityManager.createNativeQuery(sql);
        setParameter(query, params);
        return query.executeUpdate();
    }

    /**
     * 将查询结果封装为List<Map<String,Object>>对象
     *
     * @param sql         sql语句
     * @param params      参数
     * @param transformer 结果转换器
     * @return 返回sql结果
     */
    @SuppressWarnings("deprecation")
    public List<Map<String, Object>> listMap(String sql, Map<String, Object> params, ResultTransformer transformer) {
        Query query = entityManager.createNativeQuery(sql);
        query.unwrap(org.hibernate.query.Query.class).setResultTransformer(transformer);
        this.setParameter(query, params);
        return query.getResultList();
    }

    /**
     * 将查询结果封装为List<Map<String,Object>>对象
     *
     * @param sql    sql语句
     * @param params 参数
     * @return 返回sql结果
     */
    public List<Map<String, Object>> listMap(String sql, Map<String, Object> params) {
        return this.listMap(sql, params, CamelCaseToMapResultTransformer.INSTANCE);
    }

    /**
     * 将查询结果封装为List<Map<String,Object>>对象
     *
     * @param sql         sql语句
     * @param transformer 结果转换器
     * @return 返回sql结果
     */
    public List<Map<String, Object>> listMap(String sql, ResultTransformer transformer) {
        return this.listMap(sql, new HashMap<>(2), transformer);
    }

    /**
     * 将查询结果封装为List<Map<String,Object>>对象
     *
     * @param sql sql语句
     * @return 返回sql结果
     */
    public List<Map<String, Object>> listMap(String sql) {
        return this.listMap(sql, new HashMap<>(2));
    }

    /**
     * 将查询结果封装为List<Map<String,Object>>对象---since 2021-06-07 修改未查询到结果初始化一个map返回
     *
     * @param sql    sql语句
     * @param params 参数
     * @return 返回sql结果
     */
    public Map<String, Object> map(String sql, Map<String, Object> params) {
        return this.map(sql, params, CamelCaseToMapResultTransformer.INSTANCE);
    }

    /**
     * 将查询结果封装为List<Map<String,Object>>对象---since 2021-06-07 修改未查询到结果初始化一个map返回
     *
     * @param sql    sql语句
     * @param params 参数
     * @return 返回sql结果
     */
    @SuppressWarnings("deprecation")
    public Map<String, Object> map(String sql, Map<String, Object> params, ResultTransformer transformer) {
        Query query = entityManager.createNativeQuery(sql);
        query.unwrap(org.hibernate.query.Query.class).setResultTransformer(transformer);
        this.setParameter(query, params);
        try {
            return (Map<String, Object>) query.getSingleResult();
        } catch (NoResultException e) {
            return new HashMap<>(1);
        }
    }

    /**
     * 将查询结果封装为T对象
     * since 2021-06-07 结果未查询到时返回Optional.empty();
     *
     * @param sql         sql语句
     * @param elementType 返回结果集元素类型
     */
    public <T> Optional<T> queryV1(String sql, Class<T> elementType) {
        try {
            T rs = this.query(sql, HashMap::new, elementType);
            return Optional.of(rs);
        } catch (NoResultException e) {
            return Optional.empty();
        }
    }

    /**
     * 将查询结果封装为T对象
     * since 2021-06-07 结果未查询到时返回Optional.empty();
     *
     * @param sql         sql语句
     * @param params      参数
     * @param elementType 返回结果集元素类型
     */
    public <T> Optional<T> queryV1(String sql, Map<String, Object> params, Class<T> elementType) {
        try {
            T rs = this.query(sql, () -> params, elementType);
            return Optional.of(rs);
        } catch (NoResultException e) {
            return Optional.empty();
        }
    }

    /**
     * 将查询结果封装为T对象
     * since 2023-05-31 结果未查询到时返回null
     *
     * @param sql         sql语句
     * @param elementType 返回结果集元素类型
     */
    public <T> T queryV2(String sql, Class<T> elementType) {
        try {
            return this.query(sql, HashMap::new, elementType);
        } catch (NoResultException e) {
            return null;
        }
    }

    /**
     * 将查询结果封装为T对象
     * since 2023-05-31 结果未查询到时返回null
     *
     * @param sql         sql语句
     * @param params      参数
     * @param elementType 返回结果集元素类型
     */
    public <T> T queryV2(String sql, Map<String, Object> params, Class<T> elementType) {
        try {
            return this.query(sql, () -> params, elementType);
        } catch (NoResultException e) {
            return null;
        }
    }

    /**
     * 将查询结果封装为T对象
     *
     * @param sql         sql语句
     * @param elementType 返回结果集元素类型
     */
    public <T> T query(String sql, Class<T> elementType) {
        return this.query(sql, HashMap::new, elementType);
    }

    /**
     * 将查询结果封装为T对象
     *
     * @param sql         sql语句
     * @param params      参数
     * @param elementType 返回结果集元素类型
     */
    public <T> T query(String sql, Map<String, Object> params, Class<T> elementType) {
        return this.query(sql, () -> params, elementType);
    }

    /**
     * 将查询结果封装为List<T>对象
     *
     * @param sql         sql语句
     * @param params      参数
     * @param elementType 返回结果集元素类型
     */
    public <T> List<T> list(String sql, Map<String, Object> params, Class<T> elementType) {
        return list(sql, () -> params, elementType);
    }

    /**
     * 将查询结果封装为List<T>对象
     *
     * @param sql         sql语句
     * @param elementType 返回结果集元素类型
     */
    public <T> List<T> list(String sql, Class<T> elementType) {
        return this.list(sql, HashMap::new, elementType);
    }

    /**
     * 将查询结果封装为List<T>对象
     *
     * @param pageable    pageable 分页对象
     * @param sql         sql语句
     * @param params      参数
     * @param elementType 返回结果集元素类型
     */
    public <T> List<T> list(Pageable pageable, String sql, Map<String, Object> params, Class<T> elementType) {
        return this.list(pageable, sql, () -> params, elementType);
    }

    /**
     * 将查询结果封装为List<T>对象
     *
     * @param pageable    pageable 分页对象
     * @param sql         sql语句
     * @param elementType 返回结果集元素类型
     */
    public <T> List<T> list(Pageable pageable, String sql, Class<T> elementType) {
        return this.list(pageable, sql, HashMap::new, elementType);
    }

    /**
     * 将查询结果封装为List<T>对象
     *
     * @param queryCondition 动态查询语句构建对象
     */
    public <T> List<T> list(QueryCondition queryCondition, Class<T> elementType) {
        String sql = queryCondition.getQuerySql();
        Map<String, Object> params = queryCondition.getParameters();
        Pageable pageable = queryCondition.getPageable();
        if (pageable != null) {
            return this.list(pageable, sql, params, elementType);
        } else {
            return this.list(sql, params, elementType);
        }
    }

    /**
     * 将查询结果封装为page<T>对象
     *
     * @param queryCondition 动态查询语句构建对象
     */
    public <T> Page<T> page(QueryCondition queryCondition, Class<T> elementType) {
        String sql = queryCondition.getQuerySql();
        Map<String, Object> params = queryCondition.getParameters();
        Pageable pageable = queryCondition.getPageable();
        return this.page(pageable, sql, params, elementType);
    }

    /**
     * 将查询结果封装为Page<Map<String, Object>对象
     *
     * @param pageable pageable 分页对象
     * @param sql      sql语句
     * @param params   参数
     */
    public Page<Map<String, Object>> page(Pageable pageable, String sql, Map<String, Object> params) {
        Query query = getQuery(pageable, sql, () -> params, Map.class);
        List<Map<String, Object>> rsList = query.getResultList();
        return PageableExecutionUtils.getPage(rsList, pageable, () -> this.count(sql, params));
    }

    /**
     * 将查询结果封装为Page<Map<String, Object>对象
     *
     * @param pageable    pageable 分页对象
     * @param sql         sql语句
     * @param params      参数
     * @param transformer 结果转换器
     */
    @SuppressWarnings("deprecation")
    public Page<Map<String, Object>> page(Pageable pageable, String sql, Map<String, Object> params,
                                          ResultTransformer transformer) {
        Query query = getQuery(pageable, sql, () -> params, Map.class);
        query.unwrap(org.hibernate.query.Query.class).setResultTransformer(transformer);
        List<Map<String, Object>> rsList = query.getResultList();
        return PageableExecutionUtils.getPage(rsList, pageable, () -> this.count(sql, params));
    }

    /**
     * 将查询结果封装为Page<T>对象
     *
     * @param pageable    pageable 分页对象
     * @param sql         sql语句
     * @param params      参数
     * @param elementType 返回结果集元素类型
     */
    public <T> Page<T> page(Pageable pageable, String sql, Map<String, Object> params, Class<T> elementType) {
        return page(pageable, sql, () -> params, elementType);
    }

    /**
     * 获取记录总数
     *
     * @param querySql sql语句
     * @param params   参数
     */
    public long count(String querySql, Map<String, Object> params) {
        return this.count(querySql, () -> params);
    }

    /**
     * 获取记录总数
     *
     * @param countSql sql语句
     */
    public long count(String countSql) {
        return this.count(countSql, HashMap::new);
    }

    /**
     * 获取记录总数
     *
     * @param queryCondition 动态查询语句构建对象
     */
    public long count(QueryCondition queryCondition) {
        String sql = queryCondition.getQuerySql();
        Map<String, Object> params = queryCondition.getParameters();
        return this.count(sql, params);
    }

    /**
     * 根据id批量删除
     *
     * @param ids       需要删除的主键ID
     * @param consumers 需要删除的方法
     * @author 薛峰
     * createDate: 2021/6/17 11:07
     */
    @SafeVarargs
    public static void deleteByIds(String[] ids, Consumer<String>... consumers) {
        try {
            SailingAssert.notEmpty(ids, "主键id不能为空!");
            for (String id : ids) {
                for (Consumer<String> consumer : consumers) {
                    consumer.accept(id);
                }
            }
        } catch (Exception e) {
            throw BusinessException.businessError("批量删除数据失败！", e);
        }
    }

    /**
     * 获取数据库当前时间
     * <p>
     * 本方法通过执行SQL查询语句来获取数据库服务器的当前时间，以确保时间的准确性
     * 主要用于需要与数据库时间同步的场景，比如时间戳的生成、数据一致性校验等
     *
     * @return Date 返回数据库服务器的当前时间
     * @throws BusinessException 当查询数据库时间发生错误时，抛出业务异常
     */
    public Date getDbTime() {
        try {
            // SQL查询语句，用于获取当前时间
            String sql = "select now()";
            // 执行查询并返回结果，query方法的具体实现未在代码中给出，假设其能够根据SQL查询并返回相应类型的结果
            return this.query(sql, Date.class);
        } catch (Exception e) {
            // 当查询过程中发生异常时，抛出自定义的业务异常，提供更具体的错误信息
            throw BusinessException.businessError("查询数据库当前时间出错！", e);
        }
    }

    /**
     * 查询结果为一个对象
     *
     * @param querySql       查询语句
     * @param paramsSupplier 注入参数生产函数
     * @param elementType    返回结果元素类型
     * @param <T>            返回结果元素类型
     */
    protected <T> T query(String querySql, Supplier<Object> paramsSupplier, Class<T> elementType) {
        Query query = this.getNativeQuery(querySql, elementType, paramsSupplier);
        return (T) query.getSingleResult();
    }

    /**
     * 将查询结果封装为Page<T>对象
     *
     * @param pageable       pageable 分页对象
     * @param sql            sql语句
     * @param paramsSupplier 参数
     * @param elementType    返回结果集元素类型
     */
    protected <T> Page<T> page(Pageable pageable, String sql, Supplier<Object> paramsSupplier, Class<T> elementType) {
        Query query = getQuery(pageable, sql, paramsSupplier, elementType);
        List<T> rsList = query.getResultList();
        return PageableExecutionUtils.getPage(rsList, pageable, () -> this.count(sql, paramsSupplier));
    }

    /**
     * 将查询结果封装为List<T>对象
     *
     * @param pageable       pageable分页对象
     * @param sql            sql语句
     * @param paramsSupplier 获取参数的生成函数
     * @param elementType    返回结果集元素类型
     */
    protected <T> List<T> list(Pageable pageable, String sql, Supplier<Object> paramsSupplier, Class<T> elementType) {
        Query query = getQuery(pageable, sql, paramsSupplier, elementType);
        return query.getResultList();

    }

    /**
     * 将查询结果封装为List<T>对象
     *
     * @param querySql       sql语句
     * @param paramsSupplier 获取参数的生成函数
     * @param elementType    返回结果集元素类型
     */
    protected <T> List<T> list(String querySql, Supplier<Object> paramsSupplier, Class<T> elementType) {
        Query query = this.getNativeQuery(querySql, elementType, paramsSupplier);
        return query.getResultList();
    }

    /**
     * 查询统计
     *
     * @param querySql       查询语句
     * @param paramsSupplier 获取参数的生成函数
     */
    protected Long count(String querySql, Supplier<Object> paramsSupplier) {
        String countSql = SqlUtils.getCountSql(querySql);
        Query query = entityManager.createNativeQuery(countSql);
        this.setParameter(query, paramsSupplier.get());
        Object count = query.getSingleResult();
        return new DataConverter().convert(count, Long.class);
    }

    /**
     * 获取一个查询的query对象
     *
     * @param pageable       分页参数
     * @param sql            sql语句
     * @param paramsSupplier 参数信息
     * @param elementType    返回值类型
     * @param <T>            结果集类型
     * @return query对象
     */
    private <T> Query getQuery(Pageable pageable, String sql, Supplier<Object> paramsSupplier, Class<T> elementType) {
        String querySql = SqlUtils.getQuerySql(sql, pageable.getSort());
        Query query = this.getNativeQuery(querySql, elementType, paramsSupplier);
        if (pageable.isPaged()) {
            query.setFirstResult((int) pageable.getOffset());
            query.setMaxResults(pageable.getPageSize());
        }
        return query;
    }

    /**
     * 生成查询对象
     *
     * @param sql            sql语句
     * @param paramsSupplier 获取参数的生成函数
     */
    @SuppressWarnings("deprecation")
    private Query getNativeQuery(String sql, Class<?> elementType, Supplier<Object> paramsSupplier) {
        Query query = entityManager.createNativeQuery(sql);
        org.hibernate.query.Query<?> unwrap = query.unwrap(org.hibernate.query.Query.class);
        if (elementType == Map.class) {
            unwrap.setResultTransformer(CamelCaseToMapResultTransformer.INSTANCE);
        } else if (ClassUtils.isNumberType(elementType) || ClassUtils.isStringType(elementType) || ClassUtils.isBooleanType(elementType) || Date.class.isAssignableFrom(elementType)) {
            unwrap.setResultTransformer(new XtPrimitiveWrapperResultTransformer(elementType));
        } else {
            unwrap.setResultTransformer(new AliasToBeanResultTransformer(elementType));
        }
        this.setParameter(query, paramsSupplier.get());
        return query;
    }

    /**
     * 参数设置
     *
     * @param query  查询函数
     * @param params 注入参数
     */
    private void setParameter(Query query, Object params) {
        if (params == null) {
            return;
        }
        if (params instanceof Map) {
            SqlUtils.setParameter(query, (Map<String, Object>) params);
        } else if (params instanceof List) {
            SqlUtils.setParameter(query, (List<Object>) params);
        } else {
            throw new ConvertException("不支持的注入参数类型：" + params.getClass().getName());
        }
    }
}
